﻿using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Web.Security;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data;
namespace SSCCMembership.Web.Services.Security
{
    [ErrorHandlerBehavior]
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class UserManagement
    {

        //public DataModel DataContext = new DataModel();

        [OperationContract]
        public User[] GetUsers()
        {
            SecurityTester.AssertRoles("EditUsers");
            return Membership.GetAllUsers().OfType<MembershipUser>().Select((MU)=>new User()
            {
                 UserName=MU.UserName,
                 ID = MU.UserName.GetHashCode(),
                 Password="",
                 Comment=MU.Comment,
                 Roles = SSCCDatabase
                     .Query("SELECT RoleID FROM SSCC_SEC_UserRoles WHERE UserName=@UserName", 
                     new SqlParameter("@UserName",SqlDbType.VarChar), MU.UserName)
                     .Select((R) => (int)R["RoleID"]).ToArray()//DataContext.SSCC_SEC_UserRoles.Where((UR)=>UR.UserName==MU.UserName).Select((UR)=>UR.SSCC_SEC_Roles.ID).ToArray()
            }).ToArray();
        }
        [OperationContract]
        public Role[] GetRoles()
        {
            SecurityTester.AssertRoles("EditRoles");
            using (var Q = new Query("SELECT * FROM SSCC_SEC_Roles",new Dictionary<SqlParameter, object>()))
            {
                return Q.Select((R) =>
                {
                    var Ro = new Role();
                    Ro.ID = (int)R["ID"];
                    Ro.Name = R["Name"].ToString();
                    Ro.Notes = R["Notes"].ToString();
                    using (var aQ = new Query("SELECT AbilityID FROM SSCC_SEC_RoleAbilities WHERE RoleID=@RID", 
                        new Dictionary<SqlParameter,object>(){
                        {new SqlParameter("@RID", System.Data.SqlDbType.Int), R["ID"]}
                    }))
                    {
                        Ro.Abilities = aQ.Select((A) => (int)A["AbilityID"]).ToArray();
                    }
                    return Ro;
                }).ToArray();
            }
            //return DataContext.SSCC_SEC_Roles.Select(RoleConverter).ToArray();
        }
        //Role RoleConverter(SSCC_SEC_Roles R)
        //{
        //    R.SSCC_SEC_RoleAbilities.Load();
        //    return new Role(R.ID, R.Name, R.Notes, R.SSCC_SEC_RoleAbilities.Select(AbilityID).ToArray());
        //}
        //int AbilityID(SSCC_SEC_RoleAbilities A)
        //{  
        //    A.SSCC_SEC_AbilitiesReference.Load();
        //    return A.SSCC_SEC_AbilitiesReference.Value.ID; 
        //}
        [OperationContract]
        public Ability[] GetAbilities()
        {
            SecurityTester.AssertRoles("EditAbilities");
            return SSCCDatabase.Query("SELECT * FROM SSCC_SEC_Abilities").Select((R) => new Ability() { ID = (int)R["ID"], Name = R["Name"].ToString() }).ToArray();
        }

        [OperationContract]
        public int NewAbility(Ability A)
        {
            SecurityTester.AssertRoles("EditAbilities");
            
            if(SSCCDatabase.ExecuteStatement("INSERT INTO SSCC_SEC_Abilities (Name) Values (@Name)",new SqlParameter("@Name",SqlDbType.VarChar), A.Name) == 1)
                return SSCCDatabase.HighestID("SSCC_SEC_Abilities");
            throw new Exception("Failed to create Ability");
            //var ANew = new SSCC_SEC_Abilities() { Name = A.Name };
            //DataContext.AddToSSCC_SEC_Abilities(ANew);
            //DataContext.SaveChanges();
            //return ANew.ID;
        }

        [OperationContract]
        public int NewRole(Role R)
        {
            SecurityTester.AssertRoles("EditRoles");

            int RoleID = 0;
            if (SSCCDatabase.ExecuteStatement("INSERT INTO SSCC_SEC_Roles (Name, Notes) Values (@Name, @Notes)",
                new Dictionary<SqlParameter, object>()
                {
                    {new SqlParameter("@Name", SqlDbType.VarChar), R.Name},
                    {new SqlParameter("@Notes", SqlDbType.VarChar), R.Notes}
                }) == 1)
                RoleID = SSCCDatabase.HighestID("SSCC_SEC_Roles");

            foreach (var item in R.Abilities)
            {
                SSCCDatabase.ExecuteStatement("INSERT INTO SSCC_SEC_RoleAbilities (RoleID, AbilityID) VALUES (@RoleID, @AbilityID)",
                    new Dictionary<SqlParameter, object>()
                    {
                        {new SqlParameter("@RoleID", SqlDbType.Int), RoleID},
                        {new SqlParameter("@AbilityID", SqlDbType.Int), item}
                    });
            }

            return RoleID;
            //var RNew = new SSCC_SEC_Roles() { Name = R.Name, Notes = R.Notes };
            //RNew.SSCC_SEC_RoleAbilities = new System.Data.Objects.DataClasses.EntityCollection<SSCC_SEC_RoleAbilities>();
            //var RA = DataContext.SSCC_SEC_Abilities.AsEnumerable().Where((A) => R.Abilities.Contains(A.ID)).Select((A) => new SSCC_SEC_RoleAbilities()
            //{
            //    SSCC_SEC_Roles = RNew,
            //    SSCC_SEC_Abilities = A
            //});
            //foreach (var RoleAbility in RA)
            //{
            //    DataContext.AddToSSCC_SEC_RoleAbilities(RoleAbility);
            //    RNew.SSCC_SEC_RoleAbilities.Add(RoleAbility);
            //}

            //DataContext.AddToSSCC_SEC_Roles(RNew);
            //DataContext.SaveChanges();
            //return RNew.ID;
        }

        [OperationContract]
        public void NewUser(User U)
        {
            SecurityTester.AssertRoles("EditUsers");
            string Pass = U.Password;
            //An arbitary e-mail is set since we don't care
            //emails are stored against the MEMBER
            MembershipCreateStatus MCS;
            var MU = Membership.CreateUser(U.UserName, Pass, "foo@foo.co.uk","Question", "SecureAnswer:5626:4565:0561", true, out MCS);
            if (MCS == MembershipCreateStatus.Success)
            {
                MU.Comment = U.Comment;

                foreach (var item in U.Roles)
                {
                    SSCCDatabase.ExecuteStatement("INSERT INTO SSCC_SEC_UserRoles (UserName, RoleID) VALUES (@UserName, @RoleID)",
                    new Dictionary<SqlParameter, object>()
                    {
                        {new SqlParameter("@UserName", SqlDbType.VarChar), U.UserName},
                        {new SqlParameter("@RoleID", SqlDbType.Int), item}
                    });
                }

                //var UserRoles = DataContext.SSCC_SEC_Roles.AsEnumerable().Where((R) => U.Roles.Contains(R.ID))
                //    .Select((R) => new SSCC_SEC_UserRoles()
                //    {
                //        SSCC_SEC_Roles = R,
                //        UserName = U.UserName
                //    });
                //foreach (var UserRole in UserRoles)
                //{
                //    DataContext.AddToSSCC_SEC_UserRoles(UserRole);
                //}
                //DataContext.SaveChanges();
                //EmailUser(U, Pass);
            }
        }

        //private void EmailUser(User U, string Pass)
        //{
        //    //System.Net.Mail.MailMessage MM = new System.Net.Mail.MailMessage("dee@lindesay.co.uk", U.email, "SSCC Membership Username",
        //    //    String.Format(
        //    //    "Your account has been created for the SSCC Memberhip System.\n\r"+
        //    //    "To login use the following username and password\n\r"+
        //    //    "Username:{0}\n\rPassword:{1}\n\r"+
        //    //    "It is recommended that you keep this e-mail for reference.",
        //    //    U.UserName, Pass));
        //    //System.Net.Mail.SmtpClient SC = new System.Net.Mail.SmtpClient("mailhost.zen.co.uk");
        //    //SC.Send(MM);
        //}

        [OperationContract]
        public void AddAbilityToRole(int RoleID, int AbilityID)
        {
            SecurityTester.AssertRoles("EditRoles"); //Part of the edit roles tab
            bool AlreadyExists = false;
            using (var Q = SSCCDatabase.Query("SELECT ID FROM SSCC_SEC_RoleAbilities WHERE RoleID=@RoleID AND AbilityID=@AbilityID", new Dictionary<SqlParameter, object>() { 
                {new SqlParameter("@RoleID",SqlDbType.Int), RoleID},
                {new SqlParameter("@AbilityID", SqlDbType.Int), AbilityID}
            }))
            {
                AlreadyExists = Q.Any();
            }
            if (!AlreadyExists)
            {
                SSCCDatabase.ExecuteStatement("INSERT INTO SSCC_SEC_RoleAbilities (RoleID, AbilityID) VALUES (@RoleID, @AbilityID)",
                    new Dictionary<SqlParameter, object>()
                    {
                        {new SqlParameter("@RoleID", SqlDbType.Int), RoleID},
                        {new SqlParameter("@AbilityID", SqlDbType.Int), AbilityID}
                    });
            }
        }
        [OperationContract]
        public void RemoveAbilityFromRole(int RoleID, int AbilityID)
        {
            SecurityTester.AssertRoles("EditRoles"); //Part of the edit roles tab
            SSCCDatabase.ExecuteStatement("DELETE FROM SSCC_SEC_RoleAbilities WHERE RoleID=@RoleID AND AbilityID=@AbilityID)",
                new Dictionary<SqlParameter, object>()
                    {
                        {new SqlParameter("@RoleID", SqlDbType.Int), RoleID},
                        {new SqlParameter("@AbilityID", SqlDbType.Int), AbilityID}
                    });
        }

        [OperationContract]
        public void AddRoleToUser(string UserName, int RoleID)
        {

            SecurityTester.AssertRoles("EditUsers"); //Part of the edit users tab
            bool AlreadyExists = false;
            using (var Q = SSCCDatabase.Query("SELECT ID FROM SSCC_SEC_UserRoles WHERE RoleID=@RoleID AND UserName=@UserName", new Dictionary<SqlParameter, object>() { 
                {new SqlParameter("@RoleID",SqlDbType.Int), RoleID},
                {new SqlParameter("@UserName", SqlDbType.VarChar), UserName}
            }))
            {
                AlreadyExists = Q.Any();
            }
            if (!AlreadyExists)
            {
                SSCCDatabase.ExecuteStatement("INSERT INTO SSCC_SEC_UserRoles (UserName, RoleID) VALUES (@UserName, @RoleID)",
                    new Dictionary<SqlParameter, object>()
                    {
                        {new SqlParameter("@RoleID", SqlDbType.Int), RoleID},
                        {new SqlParameter("@UserName", SqlDbType.VarChar), UserName}
                    });
            }
        }
        [OperationContract]
        public void RemoveRoleFromUser(string UserName, int RoleID)
        {
            SecurityTester.AssertRoles("EditUsers"); //Part of the edit users tab
            SSCCDatabase.ExecuteStatement("DELETE FROM SSCC_SEC_UserRoles WHERE RoleID=@RoleID AND UserName=@UserName)",
                new Dictionary<SqlParameter, object>()
                    {
                        {new SqlParameter("@RoleID", SqlDbType.Int), RoleID},
                        {new SqlParameter("@UserName", SqlDbType.VarChar), UserName}
                    });
        }

        [OperationContract]
        public void UpdateRolePropety(int ID, string PropertyName, object Value)
        {
            SecurityTester.AssertRoles("EditRoles");
            switch (PropertyName)
            {
                case "Name":
                case "Notes":
                    SSCCDatabase.ExecuteStatement("UPDATE SSCC_SEC_Roles SET " + PropertyName + "=@value WHERE ID=@ID",
                        new Dictionary<SqlParameter, object>()
                        {
                            {new SqlParameter("@value", SqlDbType.VarChar), Value},
                            {new SqlParameter("@ID", SqlDbType.Int), ID}
                        });
                    break;
                default:
                    break;
            }
            //var Role = DataContext.SSCC_SEC_Roles.First((R) => R.ID == ID);
            //if (PropertyName == "Name")
            //{
            //    Role.Name = Value.ToString();
            //}
            //else if (PropertyName == "Notes")
            //{
            //    Role.Notes = Value.ToString();
            //}
            //DataContext.SaveChanges();
        }

        //[OperationContract]
        //public void SetUserEmail(string Username, string Email)
        //{
        //    SecurityTester.AssertRoles("EditUsers");
        //    var MU = Membership.GetUser(Username);
        //    MU.Email = Email;
        //    Membership.UpdateUser(MU);
        //}
        [OperationContract]
        public void SetUserComment(string Username, string Comment)
        {
            SecurityTester.AssertRoles("EditUsers");
            var MU = Membership.GetUser(Username);
            MU.Comment = Comment;
            Membership.UpdateUser(MU);
        }
    }

    public class Ability
    {
        [DataMember]
        public int ID { get; set; }

        [DataMember]
        public string Name { get; set; }
    }
    public class Role
    {
        public Role()
        {

        }
        public Role(int ID, string Name, string Notes, int[] Abilities)
        {
            this.ID = ID;
            this.Name = Name;
            this.Abilities = Abilities;
        }

        [DataMember]
        public int ID { get; set; }

        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public string Notes { get; set; }

        [DataMember]
        public int[] Abilities { get; set; }
    }


    [DataContract]
    public class User
    {
        [DataMember]
        public int ID { get; set; }

        [DataMember]
        public string UserName { get; set; }

        //Only used for the New User Method
        [DataMember]
        public string Password { get; set; }

        //[DataMember]
        //public string email { get; set; }

        [DataMember]
        public string Comment { get; set; }

        [DataMember]
        public int[] Roles { get; set; }
    }
}
